#include <stdio.h>
#include <fcntl.h>


/* when split */
#define piece_length (200*1024)
/* #define piece_length (1000) */


/* 4 bytes coding to 5 bytes */
void quad_to_quin(unsigned int val, char *buf)
{
  int i;
  for(i=0;i<5;i++)
    {
      buf[i] = (val % 93) + 0x21;
      val /= 93;
    }
}

void quin_to_quad(unsigned int *val, char *buf)
{
  int i;
  *val = 0;
  for(i=4;i>=0;i--)
    {
      *val = (*val * 93) + (buf[i]-0x21);
    }
}


void usage_exit()
{
  printf("usage : ./convert recode origfile -o result.txt\n");
  printf("usage : ./convert decode result.txt -o origfile\n");
  exit(0);
}

typedef union {
    char buf[4];
    int val;
} block_t;


void recoding_file(int fr, int fw)
{
  int ret;
  char cvtbuf[5];
  block_t block;
  long int length = 0;
  
  while( (ret = read(fr, block.buf, 4)) > 0)
    {
      quad_to_quin(block.val, cvtbuf);
      write(fw, cvtbuf, 5);
      length += 5;
      if(length%piece_length==0)
        write(fw, "\n", 1);
        
      if(ret < 4)
        break;                  /* the last block */
    }
  if(ret < 0)
    {
      printf("read source file error\n");
      exit(0);
    }

  if(ret == 0)
    ret = 4;

  quad_to_quin(ret, cvtbuf);
  write(fw, cvtbuf, 5);
}

void decoding_file(int fr, int fw)
{
  int ret;
  char cvtbuf[5];
  block_t fifo[2];
  int i = 0;
  
  while( (ret = read(fr, cvtbuf, 5)) == 5 )
    {
      quin_to_quad(&fifo[i].val, cvtbuf);
      i++;
      if(i == 2)
        {
          write(fw, fifo[0].buf, 4);
          fifo[0] = fifo[1];
          i = 1;
        }
    }
  if(ret < 0)
    {
      printf("read source file error\n");
      exit(0);
    }
  if(ret)
    {
      printf("source file format error(not alignment) !\n");
      exit(0);
    }
  if(i<1)
    {
      printf("source file format error(too short) !\n");
      exit(0);
    }
  /* process the last block */
  write(fw, fifo[0].buf, fifo[1].val);
}



int main(int argc, char *argv[])
{
  int fr, fw;
  int ret;
  int recode;

  if(argc < 5)
    usage_exit();
  
  if(strcmp(argv[1], "recode") == 0)
    recode = 1;
  else if(strcmp(argv[1], "decode") == 0)
    recode = 0;
  else
    usage_exit();

  if(strcmp(argv[3], "-o"))
    usage_exit();

  fr = open(argv[2], O_RDONLY);
  fw = open(argv[4], O_WRONLY | O_CREAT, S_IRWXU);
    
  if(fr < 0)
    {
      perror(argv[2]);
      exit(-1);
    }

  if(fw < 0)
    {
      perror(argv[4]);
      exit(-1);
    }

  if(recode)
    recoding_file(fr, fw);
  else
    decoding_file(fr, fw);

  close(fr);
  close(fw);
}


